// DnDUtils.h
// 2009/06/04

#pragma once

namespace DragDrop {

// Medium
class Medium {

	STGMEDIUM m_med;

public:

	Medium()
	{
		ZeroMemory(&m_med, sizeof(m_med));
	}

	~Medium()
	{
		if (m_med.tymed != 0) {
			ReleaseStgMedium(&m_med);
		}
	}

	STGMEDIUM* operator&()
	{
		return &m_med;
	}

	STGMEDIUM* operator->()
	{
		return &m_med;
	}

}; // Medium

// Utils
class Utils {

private:

	Utils()
	{
	}

public:

	static HRESULT SetData(
		IDataObject* dobj,
		CLIPFORMAT   cf,
		const void*  data,
		SIZE_T       size)
	{
		if (dobj == 0) {
			return E_INVALIDARG;
		}

		HGLOBAL hGlobal = GlobalAlloc(GPTR, size);
		if (hGlobal == 0) {
			return E_OUTOFMEMORY;
		}

		if (data != 0 && size > 0) {
			CopyMemory(hGlobal, data, size);
		}

		FORMATETC fmt = { cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };

		STGMEDIUM med = { 0 };
		med.tymed    = TYMED_HGLOBAL;
		med.hGlobal  = hGlobal;

		HRESULT hRslt = dobj->SetData(
			&fmt,
			&med,
			TRUE);
		if (FAILED(hRslt)) {
			GlobalFree(hGlobal);
			return hRslt;
		}

		return S_OK;
	}

	static HRESULT SetData(
		IDataObject*    dobj,
		CLIPFORMAT      cf,
		const CStringW& data)
	{
		HRESULT hRslt = SetData(
			dobj,
			cf,
			static_cast<LPCWSTR>(data),
			data.GetLength() * sizeof(WCHAR) + sizeof(WCHAR));
		if (FAILED(hRslt)) {
			return hRslt;
		}

		return S_OK;
	}

	static HRESULT QueryData(
		IDataObject* dobj,
		CLIPFORMAT   cf)
	{
		if (dobj == 0) {
			return E_INVALIDARG;
		}

		FORMATETC fmt = { cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
		HRESULT hRslt = dobj->QueryGetData(
			&fmt);
		if (FAILED(hRslt)) {
			return hRslt;
		}

		return S_OK;
	}

	static HRESULT GetData(
		IDataObject* dobj,
		CLIPFORMAT   cf,
		CStringW&    data)
	{
		data.Empty();

		if (dobj == 0) {
			return E_INVALIDARG;
		}

		FORMATETC fmt = { cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };

		Medium med;
		HRESULT hRslt = dobj->GetData(
			&fmt,
			&med);
		if (FAILED(hRslt)) {
			return hRslt;
		}

		if (med->tymed != TYMED_HGLOBAL) {
			return DV_E_TYMED;
		}

		void* ss = GlobalLock(med->hGlobal);
		if (ss == 0) {
			return E_OUTOFMEMORY;
		}

		SIZE_T cb = GlobalSize(med->hGlobal);
		if (cb > 0) {
			int len = int(cb / sizeof(WCHAR)) - 1;
			if (len > 0) {
				data = CStringW(static_cast<LPCWSTR>(ss), len);
			}
		}
		GlobalUnlock(med->hGlobal);

		return S_OK;
	}

}; // Utils

} // namespace DragDrop

